package com.wugui.datatx.core.util;

import cn.hutool.core.util.ArrayUtil;
import com.wugui.datatx.core.biz.model.HandleProcessCallbackParam;
import com.wugui.datatx.core.log.JobLogger;
import com.wugui.datatx.core.thread.ProcessCallbackThread;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 1、内嵌编译器如"PythonInterpreter"无法引用扩展包，因此推荐使用java调用控制台进程方式"Runtime.getRuntime().exec()"来运行脚本(shell或python)；
 * 2、因为通过java调用控制台进程方式实现，需要保证目标机器PATH路径正确配置对应编译器；
 * 3、暂时脚本执行日志只能在脚本执行结束后一次性获取，无法保证实时性；因此为确保日志实时性，可改为将脚本打印的日志存储在指定的日志文件上； 4、python
 * 异常输出优先级高于标准输出，体现在Log文件中，因此推荐通过logging方式打日志保持和异常信息一致；否则用prinf日志顺序会错乱
 *
 * <p>Created by xuxueli on 17/2/25.
 */
public class ScriptUtil {

  /**
   * make script file
   *
   * @param scriptFileName
   * @param content
   * @throws IOException
   */
  public static void markScriptFile(String scriptFileName, String content) throws IOException {
    // make file,   filePath/gluesource/666-123456789.py
    try (FileOutputStream fileOutputStream = new FileOutputStream(scriptFileName)) {
      fileOutputStream.write(content.getBytes(StandardCharsets.UTF_8));
      fileOutputStream.close();
    }
  }

  /**
   * 脚本执行，日志文件实时输出
   *
   * @param command
   * @param scriptFile
   * @param logFile
   * @param params
   * @return
   */
  public static int execToFile(
      String command,
      String scriptFile,
      String logFile,
      long logId,
      long logDateTime,
      String... params) {

    FileOutputStream fileOutputStream = null;
    Thread inputThread = null;
    Thread errThread = null;
    try {
      // file
      fileOutputStream = new FileOutputStream(logFile, true);

      // command
      List<String> cmdArray = new ArrayList<>();
      cmdArray.add(command);
      cmdArray.add(scriptFile);
      if (ArrayUtil.isNotEmpty(params)) {
        cmdArray.addAll(Arrays.asList(params));
      }
      String[] cmdArrayFinal = cmdArray.toArray(new String[0]);

      // process-exec
      final Process process = Runtime.getRuntime().exec(cmdArrayFinal);

      String processId = ProcessUtil.getProcessId(process);
      JobLogger.log("------------------Process id: " + processId);
      // update task process id
      HandleProcessCallbackParam prcs =
          new HandleProcessCallbackParam(logId, logDateTime, processId);
      ProcessCallbackThread.pushCallBack(prcs);
      // log-thread
      final FileOutputStream finalFileOutputStream = fileOutputStream;
      inputThread =
          new Thread(
              () -> {
                try {
                  copy(process.getInputStream(), finalFileOutputStream, new byte[1024]);
                } catch (IOException e) {
                  JobLogger.log(e);
                }
              });
      errThread =
          new Thread(
              () -> {
                try {
                  copy(process.getErrorStream(), finalFileOutputStream, new byte[1024]);
                } catch (IOException e) {
                  JobLogger.log(e);
                }
              });
      inputThread.start();
      errThread.start();

      // process-wait
      int exitValue = process.waitFor(); // exit code: 0=success, 1=error

      // log-thread join
      inputThread.join();
      errThread.join();

      return exitValue;
    } catch (Exception e) {
      JobLogger.log(e);
      return -1;
    } finally {
      if (fileOutputStream != null) {
        try {
          fileOutputStream.close();
        } catch (IOException e) {
          JobLogger.log(e);
        }
      }
      if (inputThread != null && inputThread.isAlive()) {
        inputThread.interrupt();
      }
      if (errThread != null && errThread.isAlive()) {
        errThread.interrupt();
      }
    }
  }

  /**
   * 数据流Copy（Input自动关闭，Output不处理）
   *
   * @param inputStream
   * @param outputStream
   * @param buffer
   * @return
   * @throws IOException
   */
  private static long copy(InputStream inputStream, OutputStream outputStream, byte[] buffer)
      throws IOException {
    try {
      long total = 0;
      for (; ; ) {
        int res = inputStream.read(buffer);
        if (res == -1) {
          break;
        }
        if (res > 0) {
          total += res;
          if (outputStream != null) {
            outputStream.write(buffer, 0, res);
          }
        }
      }
      outputStream.flush();
      // out = null;
      inputStream.close();
      inputStream = null;
      return total;
    } finally {
      if (inputStream != null) {
        inputStream.close();
      }
    }
  }

  /**
   * 脚本执行，日志文件实时输出
   *
   * <p>优点：支持将目标数据实时输出到指定日志文件中去 缺点： 标准输出和错误输出优先级固定，可能和脚本中顺序不一致 Java无法实时获取
   * <!-- commons-exec -->
   * <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-exec</artifactId>
   * <version>${commons-exec.version}</version> </dependency>
   *
   * @param command
   * @param scriptFile
   * @param logFile
   * @param params
   * @return
   * @throws IOException
   */
  /*public static int execToFileB(String command, String scriptFile, String logFile, String... params) throws IOException {
      // 标准输出：print （null if watchdog timeout）
      // 错误输出：logging + 异常 （still exists if watchdog timeout）
      // 标准输入

      FileOutputStream fileOutputStream = null;   //
      try {
          fileOutputStream = new FileOutputStream(logFile, true);
          PumpStreamHandler streamHandler = new PumpStreamHandler(fileOutputStream, fileOutputStream, null);

          // command
          CommandLine commandline = new CommandLine(command);
          commandline.addArgument(scriptFile);
          if (params!=null && params.length>0) {
              commandline.addArguments(params);
          }

          // exec
          DefaultExecutor exec = new DefaultExecutor();
          exec.setExitValues(null);
          exec.setStreamHandler(streamHandler);
          int exitValue = exec.execute(commandline);  // exit code: 0=success, 1=error
          return exitValue;
      } catch (Exception e) {
          JobLogger.log(e);
          return -1;
      } finally {
          if (fileOutputStream != null) {
              try {
                  fileOutputStream.close();
              } catch (IOException e) {
                  JobLogger.log(e);
              }

          }
      }
  }*/

}
